案例名称:百度地图小应用
案例编号:C3
涉及的知识点编号:
涉及的基础实验编号:
作者:
一、项目整体概述
掌握各类基本控件的使用;
掌握Activity组件的基本用法;
进一步熟悉与掌握Fragment使用。
进一步熟悉适配器、菜单、对话框等相关概念和技术等。
熟悉百度API地图的。
二、方案设计
本项目主要是模仿美团APP的界面设计。
主要功能:
- Baidu API实现的城市选择界面。
2、首页界面
3、订单界面
4、商家界面
三、系统开发步骤
- WelComeActivity
app启动后,首先进入WelComeActivity ,在WelComeActivity中申请权限和获取远程数据
1.申请权限
使用百度地图的api来获取当前所在城市,因为需要使用自己的API_KEY,所以大家可能需要自己申请,这网上有很多介绍,这里就不多说了。这里特别要注意的一点是在android 6.0之后部分权限需要动态申请,这里一定要注意。
//判断系统版本,android 6.0之后部分权限需要动态申请
if(Build.VERSION.SDK_INT >= 23){
PermissionUtil.requestLocatePermission(this);
}
2.获取数据
启动一个AsyncTask,读取sqlite中citys表中数据的版本,正常的逻辑是拿它与服务器中的citys表版本做比较,判断是否需要更新sqlite中的citys表中的数据,这里偷懒,直接默认远程服务器中citys表版本为1,如果sqlite中版本小于1,则通过http协议获取远程服务器中的数据并写入到sqlite中。
CityLocalDao localServices = new CityLocalDao(WelComeActivity.this);
int versionNum = localServices.getVersionNum();
Log.d(FangConstant.Log_Tag,"versionNum---------"+versionNum);
if(versionNum<1) {
CityRemoteDao remoteServices = new CityRemoteDao();
List<City> cityList = remoteServices.queryAllCityList();
int count = localServices.insertIntoSql(cityList);
Log.d(FangConstant.Log_Tag,"insertIntoSql--------count:"+count);
localServices.updateVersionNum(1);
}
这里其实还有点问题,其实应该首先判断sharedpreferences中是否已经存在了已经选择的城市的数据,如果有了,就不需要再跳转到selectcity activity中。
2.SelectCityActivity
selectcityactivity主要有三个自定义View和一个ListView组成。两个自定义View为上方的ActionBar,SearchBar和右侧的SideBar,分别对应的类是ActionBar,SearchBar,ListViewSideBar。
ListVIew中的数据来自于Sqlite
private void init() {
CityLocalDao localServices = new CityLocalDao(this);
ArrayList<City> cityList = localServices.queryAllCityList();
ArrayList<City> hotCityList = localServices.queryHotCityList();
adapter = new CityListViewAdapter(this,cityList,hotCityList);
listView.setAdapter(adapter);
actionBarLeft.setOnClickListener(this);
}
ListViewSideBar中添加了一个接口OnIndexerClickListener,用于添加监听事件
sideBar.setOnIndexerClickListener(new ListViewSideBar.OnIndexerClickListener() {
@Override
public void onIndexerClick(int index, String indexStr) {
int position = adapter.getPositionForSection(indexStr.charAt(0));
listView.setSelection(position);
}
});
ListView 滑动时显示IndexToast,滑动结束后,IndexToast延迟1秒后消失
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
private String cityIndex = "";
private Handler handler = new Handler();
private Runnable run = null;
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
switch (scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
run = new Runnable() {
@Override
public void run() {
indexToast.setVisibility(View.GONE);
}
};
handler.postDelayed(run,1000);
break;
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
if(run != null) {
handler.removeCallbacks(run);
}
indexToast.setVisibility(View.VISIBLE);
break;
}
}
@Override
public void onScroll(AbsListView absListView, int i, int i2, int i3) {
if(indexToast.getVisibility()==View.VISIBLE) {
cityIndex = adapter.getCityIndex(i);
indexToast.setText(cityIndex);
}
}
});
在热门城市中使用的是自定义GridView CustomGridView,因为在ListView 中添加GridView后,GridView的高度没有自适应,需要重新测量GridView的高度
//重新测量GridView的高度
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
这里只对热门城市的Item添加了Onclick事件,获取当前选择的城市对象,并添加到SharedPreferences
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
City city = (City)gridViewAdapter.getItem(i);
Log.d(FangConstant.Log_Tag,"grid city:"+city.toString());
SharedPreferences sp = context.getSharedPreferences(FangConstant.PreferenceName, context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString(FangConstant.CurrentCityID,city.getId());
editor.putString(FangConstant.CurrentCityName,city.getName()
editor.commit();
Intent intent = new Intent(context, MainActivity.class);
context.startActivity(intent);
}
3.MainActivity
MainActivity 使用了帧布局,通过最下方的RadioGroup,实现4个Fragment的切换。
public void onCheckedChanged(RadioGroup radioGroup, int i) {
int position = 0;
switch (i) {
case R.id.rb_home: position = 0; break;
case R.id.rb_poi: position = 1; break;
case R.id.rb_user: position = 2; break;
case R.id.rb_more: position = 3; break;
}
Fragment f = mainPageAdapter.getItem(1);
Log.d(FangConstant.Log_Tag,"OnCheckedChangeListener-----------------f:"+f);
if(f != null) {
((PoiFragment)f).refresh();
}
refreshPage(position);
}
首页为 HomeFragment,也是以ListView为主要框架,ListView中共分为4类Item,第一个Item为viewpager,每个Page中是一个GridView,数据都是从DataUtil中获取,第二个Item为TableVIew,最后一个Item是一个简单的描述。中间的Item数据都是在ProListAsyncTask中处理的,首先查看sqlite中是否有数据,如果没有则从服务器中获取,并插入到sqlite中。最后在AsyncTask的onPostExecute中把数据添加到listViewAdapter中,刷新ListVIew。这里的算法逻辑和真正美团中的逻辑肯定是不一样的,大家也可以根据自己的喜好修改。这里面有个细节,从服务器中获取后的数据,在插入到sqlite时,这段代码是放在新起的线程中的。
protected ArrayList<Product> doInBackground(Void... voids) {
final ProductLocalDao localDao = new ProductLocalDao(getActivity());
ArrayList<Product> localProList = localDao.queryAllProductions();
if(localProList.size()==0) {
ProductRemoteDao remoteDao = new ProductRemoteDao();
final ArrayList<Product> proList = remoteDao.queryAllProList();
new Thread(new Runnable() {
@Override
public void run() {
localDao.insertIntoProductions(proList);
}
}).start();
return proList;
}else {
return localProList;
}
}
protected void onPostExecute(ArrayList<Product> proList) {
Log.d(FangConstant.Log_Tag,"proList-----------------size:"+proList.size());
listViewAdapter.getList().clear();
listViewAdapter.getList().addAll(proList);
listViewAdapter.notifyDataSetChanged();
}
Item中图片的加载,首先是直接从内存中获取,如果不存在,再从cache中查找,如果还没有,则去服务器中获取,得到后再写入cache和内存中。
Bitmap bitmap = ImageLoader.getInstance().getBitmapFromCache(pro.getIcon());
if(bitmap != null) {
holder.iv.setImageBitmap(bitmap);
}else {
ImageLoader.getInstance().loadBitmapFromHttp(context,holder.iv, pro.getIcon());
}
FileCache fileCache = new FileCache(context);
Btmap bitmap = fileCache.getFile(fileName);
if(bitmap == null) {
bitmap = HttpUtil.getHttpConnectionForDownloadImage(url);
}
if(bitmap != null) {
ImageLoader.instance.addBitmapToCache(fileName,bitmap);
msgMessage.arg1=1;
handler.sendMessage(msgMessage);
//先让线程通知主线程更新UI,再把图片存到本地
fileCache.saveFile(fileName,bitmap);
}
这里也是只用中间的Item有点击事件
Product pro = (Product)listViewAdapter.getItem(i);
if(pro != null) {
Intent intent = new Intent(getActivity(), PlaceOrderActivity.class);
startActivity(intent);
}
PlaceOrderActivity主要使用CustomScrollView布局,主要的知识点是使用ScrollView的onScrollChanged事件,实现订单滑动时的悬浮效果。
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(onScrollListener != null) {
onScrollListener.OnScroll(t);
}
}
public void OnScroll(int scrollY) {
int mBuyLayout2ParentTop = Math.max(scrollY, imageView.getHeight());
imageView.layout(0,0+scrollY/2,imageView.getWidth(),imageView.getHeight()+scrollY/2);
topView.layout(0, mBuyLayout2ParentTop, topView.getWidth(), mBuyLayout2ParentTop + topView.getHeight());
}
主要页面效果
城市选择界面
APP首页
订单界面
商家界面